package edu.rit.drh4818.volume;


import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;

import com.sun.j3d.utils.behaviors.mouse.*;


/**
 * The VolumeCanvas provides a drawing canvas for a VolumeRenderer. 
 * 
 * Along with adding the VolumeRenderer to the scene, this class will automatically
 * setup mouse rotation, and add animation so the Volumes' update method gets called
 * at interval.
 * 
 * Timing information can be added by setting the Settings.TIMING flag to true.
 * 
 */
@SuppressWarnings("serial")
public class VolumeCanvas extends Canvas3D implements MouseBehaviorCallback 
{
	/**
	 * Scene volume renderer.
	 */
	private VolumeRenderer renderer;
	
	/**
	 * Frame counter.
	 */
	private int numFrames = 0;
	private long startTime = System.currentTimeMillis();
	
	/**
	 * The universe.
	 */
	private SimpleUniverse universe;
	
	/**
	 * Create a new Volume renderer canvas.
	 * 
	 * @param volume Volume information to display.
	 * @param delay Delay for animation behavior.
	 * @param scale Scale factor for scene.
	 * @param background Background color.
	 */
	public VolumeCanvas(Volume volume, int delay, float scale, Color3f background) 
	{
		super( SimpleUniverse.getPreferredConfiguration() );
		setDoubleBufferEnable(true);
		
		// Create a simple scene and attach it to the virtual universe
		// Create the root of the branch graph
		BranchGroup scene = new BranchGroup();
		
		// set the background color
		Background bg = new Background( background );
		bg.setApplicationBounds( new BoundingBox() );
		scene.addChild( bg );
		
		// Create a transform group to scale the whole scene, and for
		//  rotation of the scene by the mouse behavior.
		TransformGroup scaleGroup = new TransformGroup();
		scaleGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		scaleGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
		Transform3D scaleXform = new Transform3D();
		scaleXform.setScale(scale);
		scaleGroup.setTransform(scaleXform);
		scene.addChild(scaleGroup);
		
		// Create the transform group to center abouot the origin
		TransformGroup centerGroup = new TransformGroup();
		centerGroup.setCapability(Group.ALLOW_CHILDREN_READ);
		centerGroup.setCapability(Group.ALLOW_CHILDREN_WRITE);
		centerGroup.setCapability(Group.ALLOW_CHILDREN_EXTEND);
		Transform3D centerXform = new Transform3D();
		centerXform.setTranslation(new Vector3d(-0.5, -0.5, -0.5));
		centerGroup.setTransform(centerXform);
		scaleGroup.addChild(centerGroup);

		BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
				100000.0);

		// Set up mouse rotation on the scene.
		MouseRotate mr = new MouseRotate();
		mr.setupCallback(this);
		mr.setTransformGroup(scaleGroup);
		mr.setSchedulingBounds(bounds);
		mr.setFactor(0.007);
		scene.addChild(mr);
		
		// Set up the animation behavior.
		AnimationBehavior ab = new AnimationBehavior(delay, volume);
		ab.setSchedulingBounds(bounds);
		scene.addChild(ab);
		
		// Set up the universe.
		universe = new SimpleUniverse(this);
		universe.getViewingPlatform().setNominalViewingTransform();
		View view = universe.getViewer().getView();
		
		// faster for texture mapping
		view.setProjectionPolicy(View.PARALLEL_PROJECTION);
		universe.addBranchGraph(scene);
		
		// Set up the volume renderer
		renderer = new VolumeRenderer(view, volume);
		ab.initialize( renderer );
		
		// Add the volume renderer to the universe, centered at origin.
		centerGroup.addChild( renderer );
		
		view.setProjectionPolicy(View.PERSPECTIVE_PROJECTION);
		
		renderer.update();
	}
	
    /**
     * At intervals, print out timing information.
     */
    public void postRender() 
    {
    	if( Settings.TIMING )
    	{
	    	if ((++numFrames % 100) == 0) 
	    	{
			    double framesDuration  = (System.currentTimeMillis() - startTime ) /1000F;
			    System.out.println( (int)(100 / framesDuration)  + " fps");
			    startTime = System.currentTimeMillis();
			    numFrames = 0;
			}
    	}
    }
	
	/**
	 * Update the renderer's eye point.
	 */
	public void transformChanged(int type, Transform3D xform) 
	{
		renderer.eyePtChanged();
	}
	
	/**
	 * Dispose of the canvas, cleans up the scene graph.
	 */
	public void dispose()
	{
		stopRenderer();
		universe.cleanup();
	}
}
